using System.Runtime.InteropServices;
using System.IO;
using AFEParserWPF.Models;

namespace AFEParserWPF.Services;

/// <summary>
/// 用于解析 AFE 二进制文件的服务
/// </summary>
public class AFEParser
{
    public static AFEVersion DetectVersion(byte[] data)
    {
        if (data.Length < 4)
            return AFEVersion.Unknown;

        uint magic = BitConverter.ToUInt32(data, 0);

        return magic switch
        {
            AFEConstants.ATMOSPHERE_REBOOT_TO_FATAL_MAGIC => AFEVersion.AFE2,
            AFEConstants.ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_1 => AFEVersion.AFE1,
            AFEConstants.ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0 => AFEVersion.AFE0,
            _ => AFEVersion.Unknown
        };
    }

    public static object? ParseFile(string filePath)
    {
        try
        {
            byte[] fileData = File.ReadAllBytes(filePath);
            AFEVersion version = DetectVersion(fileData);

            return version switch
            {
                AFEVersion.AFE2 => ParseAFE2(fileData),
                AFEVersion.AFE1 => ParseAFE1(fileData),
                AFEVersion.AFE0 => ParseAFE0(fileData),
                _ => null
            };
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException($"Failed to parse AFE file: {ex.Message}", ex);
        }
    }

    private static AtmosphereFatalErrorCtx? ParseAFE2(byte[] data)
    {
        int structSize = Marshal.SizeOf<AtmosphereFatalErrorCtx>();
        if (data.Length < structSize)
            return null;

        GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
        try
        {
            IntPtr ptr = handle.AddrOfPinnedObject();
            return (AtmosphereFatalErrorCtx?)Marshal.PtrToStructure(ptr, typeof(AtmosphereFatalErrorCtx));
        }
        finally
        {
            handle.Free();
        }
    }

    private static AtmosphereFatalErrorCtx1? ParseAFE1(byte[] data)
    {
        int structSize = Marshal.SizeOf<AtmosphereFatalErrorCtx1>();
        if (data.Length < structSize)
            return null;

        GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
        try
        {
            IntPtr ptr = handle.AddrOfPinnedObject();
            return (AtmosphereFatalErrorCtx1?)Marshal.PtrToStructure(ptr, typeof(AtmosphereFatalErrorCtx1));
        }
        finally
        {
            handle.Free();
        }
    }

    private static AtmosphereFatalErrorCtx0? ParseAFE0(byte[] data)
    {
        int structSize = Marshal.SizeOf<AtmosphereFatalErrorCtx0>();
        if (data.Length < structSize)
            return null;

        GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
        try
        {
            IntPtr ptr = handle.AddrOfPinnedObject();
            return (AtmosphereFatalErrorCtx0?)Marshal.PtrToStructure(ptr, typeof(AtmosphereFatalErrorCtx0));
        }
        finally
        {
            handle.Free();
        }
    }
}
